通常通过method swizzle可以交换两个方法的实现(不限于同一个类型),先看一段代码:
People类
| 12
 3
 4
 
 | - (void)talk{
 NSLog(@"%@", self.class);
 }
 
 | 
Student类继承People
Student
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | + (void)load{
 static dispatch_once_t onceToken;
 
 dispatch_once(&onceToken, ^{
 
 SEL originalSelector = NSSelectorFromString(@"talk");
 
 SEL swizzleSelector = NSSelectorFromString(@"swizzle_talk");
 
 Method originalMethod = class_getInstanceMethod(self.class, originalSelector);
 Method swizzleMethod = class_getInstanceMethod(self.class,
 swizzleSelector);
 method_exchangeImplementations(originalMethod, swizzleMethod);
 });
 }
 
 - (void)swizzle_talk
 {
 NSLog(@"swizzle_talk: %@", self.class);
 }
 
 | 
Method在objc-private.h中有如下定义:
| 1
 | typedef struct old_method *Method;
 | 
old_method是结构体,它定义在objc-runtime-old.h中:
| 12
 3
 4
 5
 
 | struct old_method {SEL method_name;
 char *method_types;
 IMP method_imp;
 }
 
 | 
Method中包含了3个部分,第一部分是函数名,通常可以通过@selector()获取,第二部分是函数声明, 第三部分是函数实现,理解成函数指针。
class_getInstanceMethod有两个参数,第一个参数是class,第二个参数是selector。这个函数是以class开头的,第一个参数也是传的class对象,所以可以理解为从所传递的类对象中查找指定的数据,类对象可以通过实例对象的class方法活的,类对象全局只有一个。
Class对象的定义如下:
| 1
 | typedef struct objc_class *Class;
 | 
也就是说Class对象其实是objc_class结构体,平时使用的self.class得到的是一个objc_class的结构体指针。
objc_class定义如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | struct objc_class : objc_object {
 Class superclass;
 
 const char *name;
 
 uint32_t version;
 
 uint32_t info;
 
 uint32_t instance_size;
 
 struct old_ivar_list *ivars;
 
 struct old_method_list **methodLists;
 
 Cache cache;
 
 struct old_protocol_list *protocols;
 
 // CLS_EXT only
 
 const uint8_t *ivar_layout;
 
 struct old_class_ext *ext;
 }
 
 | 
这里只列出了字段,函数并没有列出。可以看到一个类对象里面包含了以下比较重要的信息:
1.它的基类对象字段superclass
2.它的实例对象有哪些字段 ivars
3.它的实例对象有哪些方法,存储在方法列表中 **methodLists, 这里为什么是指针的指针,就是它可能包含多个方法列表。
4.它属于什么类型的类对象:info,比如CLS_CLASS还是CLS_META,相当于类对象自己的元数据信息。通过它可以判断出一个类对象是否是元类对象。
以下是class_getInstanceMethod的源码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 
 | Method class_getInstanceMethod(Class cls, SEL sel)
 {
 
 if (!cls  ||  !sel) return nil;
 
 
 
 
 // This deliberately avoids +initialize because it historically did so.
 
 
 
 
 // This implementation is a bit weird because it's the only place that
 
 // wants a Method instead of an IMP.
 
 
 
 
 Method meth;
 
 meth = _cache_getMethod(cls, sel, _objc_msgForward_impcache);
 
 if (meth == (Method)1) {
 
 // Cache contains forward:: . Stop searching.
 
 return nil;
 
 } else if (meth) {
 
 return meth;
 
 }
 
 
 
 // Search method lists, try method resolver, etc.
 
 lookUpImpOrNil(cls, sel, nil,
 
 NO/*initialize*/, NO/*cache*/, YES/*resolver*/);
 
 
 
 
 meth = _cache_getMethod(cls, sel, _objc_msgForward_impcache);
 
 if (meth == (Method)1) {
 
 // Cache contains forward:: . Stop searching.
 
 return nil;
 
 } else if (meth) {
 
 return meth;
 
 }
 
 
 
 
 return _class_getMethod(cls, sel);
 
 }
 
 | 
这一部分主要是先从方法缓存里取方法,主要看下_class_getMethod
| 12
 3
 4
 5
 6
 
 | static Method _class_getMethod(Class cls, SEL sel){
 mutex_locker_t lock(methodListLock);
 
 return (Method)_getMethod(cls, sel);
 }
 
 | 
在_class_getMethod中调用了_getMethod函数:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | static inline old_method * _getMethod(Class cls, SEL sel) {
 for (; cls; cls = cls->superclass) {
 
 old_method *m;
 
 m = _findMethodInClass(cls, sel);
 
 if (m) return m;
 
 }
 
 return nil;
 
 }
 
 | 
_getMethod是主要的实现了,这里通过_findMethodInClass函数来查找类对象的方法,并且便利了父类对象。也就是说,基类中的方法也会被遍历到。
继续再看下_findMethodInClass函数的代码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 
 | static inline old_method * _findMethodInClass(Class cls, SEL sel) {
 // Flattened version of nextMethodList(). The optimizer doesn't
 
 // do a good job with hoisting the conditionals out of the loop.
 
 // Conceptually, this looks like:
 
 // while ((mlist = nextMethodList(cls, &iterator))) {
 
 //     old_method *m = _findMethodInList(mlist, sel);
 
 //     if (m) return m;
 
 // }
 
 
 
 
 if (!cls->methodLists) {
 
 // No method lists.
 
 return nil;
 
 }
 
 else if (cls->info & CLS_NO_METHOD_ARRAY) {
 
 // One method list.
 
 old_method_list **mlistp;
 
 mlistp = (old_method_list **)&cls->methodLists;
 
 *mlistp = fixupSelectorsInMethodList(cls, *mlistp);
 
 return _findMethodInList(*mlistp, sel);
 
 }
 
 else {
 
 // Multiple method lists.
 
 old_method_list **mlistp;
 
 for (mlistp = cls->methodLists;
 
 *mlistp != nil  &&  *mlistp != END_OF_METHODS_LIST;
 
 mlistp++)
 
 {
 
 old_method *m;
 
 *mlistp = fixupSelectorsInMethodList(cls, *mlistp);
 
 m = _findMethodInList(*mlistp, sel);
 
 if (m) return m;
 
 }
 
 return nil;
 
 }
 
 }
 
 static inline old_method *_findMethodInList(old_method_list * mlist, SEL sel) {
 
 int i;
 
 if (!mlist) return nil;
 
 for (i = 0; i < mlist->method_count; i++) {
 
 old_method *m = &mlist->method_list[i];
 
 if (m->method_name == sel) {
 
 return m;
 
 }
 
 }
 
 return nil;
 
 }
 
 | 
这个方法主要是通过遍历类对象的方法列表字段,来查找某个方法。
在_findMethodInList函数中,它其实是比较了方法列表中方法的Selector和要找的Selector是不是同一个来查找这个方法。所以通过selector就可以定位到一个method,也就是可以得到它的IMP和Type了。
所以可以很好理解一下2个方法:
method_getTypeEncoding
method_getImplementation
通过以上分析,可以知道class_getInstanceMethod是获得某个类对象中的方法对象,这个过程中会遍历到父类中。也就是当前类没有实现的方法,父类来抵,也符合面向对象的设计。
总的说来,class_getxxxxxxx是通过查找类对象内部数据来得到一些消息,类似的还有
class_getClassMethod,它是获取类方法的函数:
看看它的源码:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | Method class_getClassMethod(Class cls, SEL sel){
 if (!cls  ||  !sel) return nil;
 return class_getInstanceMethod(cls->getMeta(), sel);
 }
 
 Class getMeta() {
 
 if (isMetaClass()) return (Class)this;
 
 else return this->ISA();
 }
 
 bool isMetaClass() {
 return info & CLS_META;
 }
 
 #define CLS_CLASS 0X1
 #define CLS_META 0x2
 
 | 
可以知道如果当前类就是元类对象,就返回它自己反之返回this→ISA();
objc_class继承自objc_object,函数ISA是objc_object中定义的:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | truct objc_object {
 private:
 
 isa_t isa;
 }
 
 uion isa_t {
 
 Class clas;
 }
 
 | 
相当于取出objc_class对象的cls信息,也就是元类对象了。
然后通过cls_getInstanceMethod来去到Method信息,跟之前取类对象中的Method一样,只是多了一步取元类对象的步骤。
在理解了class_getInstanceMethod函数之后,再来看一下class_addMethod函数:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 
 | BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
 {
 
 IMP old;
 
 if (!cls) return NO;
 
 old = _class_addMethod(cls, name, imp, types, NO);
 
 return !old;
 
 }
 static IMP _class_addMethod(Class cls, SEL name, IMP imp,
 
 const char *types, bool replace)
 {
 
 old_method *m;
 
 IMP result = nil;
 
 
 
 
 if (!types) types = "";
 
 
 
 
 mutex_locker_t lock(methodListLock);
 
 
 
 
 if ((m = _findMethodInClass(cls, name))) {
 
 // already exists
 
 // fixme atomic
 
 result = method_getImplementation((Method)m);
 
 if (replace) {
 
 method_setImplementation((Method)m, imp);
 
 }
 
 } else {
 
 // fixme could be faster
 
 old_method_list *mlist =
 
 (old_method_list *)calloc(sizeof(old_method_list), 1);
 
 mlist->obsolete = fixed_up_method_list;
 
 mlist->method_count = 1;
 
 mlist->method_list[0].method_name = name;
 
 mlist->method_list[0].method_types = strdup(types);
 
 mlist->method_list[0].method_imp = imp;
 
 
 
 _objc_insertMethods(cls, mlist, nil);
 
 if (!(cls->info & CLS_CONSTRUCTING)) {
 
 flush_caches(cls, NO);
 
 } else {
 
 // in-construction class has no subclasses
 
 flush_cache(cls);
 
 }
 
 result = nil;
 
 }
 
 return result;
 
 }
 
 | 
相当于当前类对象中存在这个方法的时候(包括父类的),什么都不会处理返回NO。如果不存在那么会添加一个,并且返回YES。
接着是class_replaceMethod
| 12
 3
 4
 5
 
 | IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types){
 if (!cls) return nil;
 return _class_addMethod(cls, name, imp, types, YES);
 }
 
 | 
该方法和class_addMethod的区别是,如果发现已经存在sel对应的Method,前者会直接通过新的imp覆盖原来的method,后者则不会做任何处理。
最后method_exchangeImplementations交换两个method的实现。
现在分析一下文章开头那段代码,当当前类本身没有实现original_selector方法的时候,但是它的基类实现了。那么最后交换的就是基类中的original_selector方法,这将会影响基类和其他继承子类的行为。现在通过一个简单的demo来验证:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 
 | @interface People : NSObject
 - (void)talk;
 
 @end
 @implementation People
 
 - (void)talk
 
 {
 
 NSLog(@"%@", self.class);
 
 }
 
 
 
 
 @interface Student : People
 
 @end
 
 @implemention Student
 
 @end
 
 
 
 @interface Teacher : People
 
 @end
 
 
 @implemention Teacher
 
 @end
 @interface Student (Tracking)
 
 @end
 
 
 @implemention Student
 
 + (void)load
 
 {
 
 static dispatch_once_t onceToken;
 
 dispatch_once(&onceToken, ^{
 
 SEL originalSelector = NSSelectorFromString(@"talk");
 
 SEL swizzleSelector = NSSelectorFromString(@"swizzle_talk");
 
 Method originalMethod = class_getInstanceMethod(self.class,
 originalSelector);
 
 Method swizzleMethod = class_getInstanceMethod(self.class,
 swizzleSelector);
 
 method_exchangeImplementations(originalMethod, swizzleMethod);
 });
 }
 
 
 
 - (void)swizzle_talk
 
 {
 
 NSLog(@"zwizzle_talk: %@", self.class);
 
 }
 
 @end
 
 - (void)viewDidLoad {
 
 [super viewDidLoad];
 
 
 Teacher *t = [[Teacher alloc] init];
 
 [t talk];
 
 Student *stu = [[Student alloc] init];
 
 [stu talk];
 
 }
 
 @end
 
 | 
输出是:
| 12
 3
 
 | 20:15:35.432 abc[87901:2148310] zwizzle_talk: Teacher
 20:15:35.433 abc[87901:2148310] zwizzle_talk: Student
 
 | 
说明 Teacher类也收到了student swizzle的影响。
Student(Tracking)换一种写法:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | + (void)load{
 static dispatch_once_t onceToken;
 
 dispatch_once(&onceToken, ^{
 
 SEL originalSelector = NSSelectorFromString(@"talk");
 
 SEL swizzleSelector = NSSelectorFromString(@"swizzle_talk");
 
 Method originalMethod = class_getInstanceMethod(self.class,
 originalSelector);
 
 Method swizzleMethod = class_getInstanceMethod(self.class,
 swizzleSelector);
 
 BOOL addMethod = class_addMethod(self.class, originalSelector,
 method_getImplementation(swizzleMethod), method_getTypeEncoding(
 swizzleMethod));
 
 if (addMethod) {
 class_replaceMethod(self.class, swizzleSelector,
 method_getImplementation(originalMethod),
 method_getTypeEncoding(originalMethod));
 } else {
 method_exchangeImplementations(originalMethod, swizzleMethod);
 }
 });
 }
 
 | 
输出是:
| 12
 3
 
 | 20:19:50.683 abc[87966:2152486] Teacher
 20:19:50.684 abc[87966:2152486] zwizzle_talk: Student
 
 | 
可以看到,Teacher类并没有收到影响,虽然是基类中实现了talk方法,但是通过class_addMethod给当前类Student动态增加了talk的实现,然后进行交换。没有影响到原来People类中的talk方法。
可以看出,第二种方法实现起来更好,影响范围更小一些。